package suray.rgv.communication;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.suray.basic.wcs.utils.ParamConfig;
import com.suray.basic.wcs.utils.WcsFuncUtil;
import com.suray.commin.exception.Rgv60CommFailException;
import com.suray.basic.wcs.utils.LoggerType;
import com.suray.commin.core.utils.StringUtils;
import com.suray.rgv.util.RgvLogger;
import com.suray.socket.SocketInterface;
import com.suray.socket.type.SocketType;
import suray.rgv.communication.data.*;
import suray.rgv.communication.data.status.*;
import suray.rgv.communication.util.SeerAgvCommPortType;
import suray.rgv.communication.util.SeerMessageType;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;

/**
 * 两栖车通讯管理，交互接口
 */
public class SeerCommManage {

    //导航通讯
    private Rgv60Comm routeComm;
    //配置通讯
    private Rgv60Comm configComm;
    //小车状态通讯
    private Rgv60Comm robotStatComm;
    //小车控制通讯
    private Rgv60Comm controlComm;
    //其他控制通讯
    private Rgv60Comm otherComm;
    //通讯地址
    private String ip;
    //小车序列号
    private String series;

    private int rgvNo;

    public SeerCommManage(String serialNum, int rgvNo) {
        this.series = serialNum;
        if (ParamConfig.isDebugModel) {
            this.rgvNo = rgvNo;
        } else {
            this.rgvNo = 0;
        }
    }

    /**
     * 通讯是否在线
     * @return
     */
    public boolean isOnLine() {
        return robotStatComm.isOnline() && routeComm.isOnline()
                && configComm.isOnline() && controlComm.isOnline();
    }

    public void init(String ip, boolean hasAuthor) throws IOException {
        if (this.ip != null && this.ip.equals(ip)) {
            if (hasAuthor) {
                clearAllFault();
                clearTask();
            }
            return;
        }

        initNet(ip);
    }

    public boolean routeOnLine() {
        if (routeComm == null) {
            return false;
        }

        return routeComm.isOnline();
    }

    public boolean configOnLine() {
        if (configComm == null) {
            return false;
        }

        return configComm.isOnline();
    }

    public boolean controlOnLine() {
        if (controlComm == null) {
            return false;
        }

        return controlComm.isOnline();
    }

    public boolean robotStateOnLine() {
        if (robotStatComm == null) {
            return false;
        }

        return robotStatComm.isOnline();
    }

    /**
     * 清除路径导航
     * @throws IOException
     */
    public void clearTask() throws IOException {
        sendClearRoute();
        sendClearAppointRout();
    }

    /**
     * 初始化网络
     * @param ip
     */
    public void initNet(String ip) {
        if (StringUtils.isNotEmpty(this.ip) && this.ip.equals(ip)) {
            return;
        }
        routeComm = initSocket(ip, SeerAgvCommPortType.ROBOT_NAVIGATE, routeComm, rgvNo);
        configComm = initSocket(ip, SeerAgvCommPortType.ROBOT_CONFIG, configComm, rgvNo);
        robotStatComm = initSocket(ip, SeerAgvCommPortType.ROBOT_STAT, robotStatComm, rgvNo);
        controlComm = initSocket(ip, SeerAgvCommPortType.ROBOT_CONTROL, controlComm, rgvNo);
        otherComm = initSocket(ip, SeerAgvCommPortType.OTHER, otherComm, rgvNo);
        this.ip = ip;
    }

    /**
     * 初始化socket
     * @param ip
     * @param type
     * @param socket
     * @return
     */
    private Rgv60Comm initSocket(String ip, SeerAgvCommPortType type, Rgv60Comm socket, int rgvNo) {
        if (socket != null) {
            socket.close();
        }
        if (ParamConfig.isDebugModel){
            return new Rgv60Comm(SocketInterface.getInstance(ip, type.getValue() + (rgvNo - 1) * 100, SocketType.CLIENT));
        } else {
            return new Rgv60Comm(SocketInterface.getInstance(ip, type.getValue(), SocketType.CLIENT));
        }
    }

    /**
     * 指定路径导航
     * @param moveTaskListDTOS
     */
    public void sendAppointRout(List<AppiontRout.MoveTaskListDTO> moveTaskListDTOS) throws IOException {
        AppiontRout route = new AppiontRout();
        route.setMove_task_list(moveTaskListDTOS);
        RgvLogger.info(LoggerType.TASK, series, "指定路径导航", route.toString(), this.getClass());
        routeComm("发送指定路径导航", route, SeerMessageType.APPOINT_ROUTE);
    }

    /**
     * 清除指定路径导航
     */
    public void sendClearAppointRout() throws IOException {
        RgvLogger.info(LoggerType.INFO, series, "清除指定路径导航", null, this.getClass());
        routeComm("清除指定路径导航", SeerMessageType.CANCEL_APPOINT_ROUTE);
    }

    /**
     * 发送路径导航
     * @param routeInfo
     * @return
     */
    public void sendRoute(RouteInfo routeInfo) throws IOException {
        RgvLogger.info(LoggerType.TASK, series, "发送路径导航", routeInfo.toString(), this.getClass());
        routeComm("发送路径导航", routeInfo, SeerMessageType.ROUTE);
    }

    /**
     * 清除路径导航
     * @return
     */
    public void sendClearRoute() throws IOException {
        RgvLogger.info(LoggerType.INFO, series, "清除路径导航", null, this.getClass());
        routeComm("清除路径导航", SeerMessageType.CANCEL_ROUTE);
    }

    /**
     * 暂停当前导航
     * @return
     */
    public void stopRoute() throws IOException {
        RgvLogger.info(LoggerType.INFO, series, "暂停当前导航", null, this.getClass());
        routeComm("暂停当前导航", SeerMessageType.STOP_ROUTE);
    }

    /**
     * 继续当前导航
     * @return
     */
    public void continueRoute() throws IOException {
        RgvLogger.info(LoggerType.INFO, series, "继续当前导航", null, this.getClass());
        routeComm("继续当前导航", SeerMessageType.CONTINUE_ROUTE);
    }

    /**
     * 获取路径导航的路径
     * @param getRoute
     * @return
     */
    public void getCurrentRoute(GetCurrentRoute getRoute) throws IOException {
        routeComm("获取路径导航的路径", getRoute, SeerMessageType.GET_CURRENT_ROUTE);
    }

    /**
     * 平动
     * @return
     */
    public void translation(Translation translation) throws IOException {
        RgvLogger.info(LoggerType.INFO, series, "平动", translation.toString(), this.getClass());
        routeComm("平动", translation, SeerMessageType.TRANSLATION);
    }

    /**
     * 转动
     * @return
     */
    public void rotate(Rotate rotate) throws IOException {
        RgvLogger.info(LoggerType.INFO, series, "转动", rotate.toString(), this.getClass());
        routeComm("转动", rotate, SeerMessageType.ROTATE);
    }

    /**
     * 托盘旋转
     * @return
     */
    public void palletRotate(PalletRotate palletRotate) throws IOException {
        RgvLogger.info(LoggerType.INFO, series, "托盘旋转", palletRotate.toString(), this.getClass());
        routeComm("托盘旋转", palletRotate, SeerMessageType.PALLET_ROTATE);
    }

    /**
     * 圆弧运动
     * @return
     */
    public void circularRotate(CircularRotate circularRotate) throws IOException {
        RgvLogger.info(LoggerType.INFO, series, "圆弧运动", circularRotate.toString(), this.getClass());
        routeComm("圆弧运动", circularRotate, SeerMessageType.CIRCLE_ROTATE);
    }

    /**
     * 查询机器人任务链
     * @return
     */
    public void robotTaskStatus(RobotTaskListStatus robotTaskListStatus) throws IOException {
        routeComm("查询机器人任务链", robotTaskListStatus, SeerMessageType.CHECK_ROBOT_TASK_STATUS);
    }

    /**
     * 执行预存任务链
     * @return
     */
    public void doRobotPreTask(DoRobotPreTask doRobotPreTask) throws IOException {
        RgvLogger.info(LoggerType.INFO, series, "执行预存任务链", doRobotPreTask.toString(), this.getClass());
        routeComm("执行预存任务链", doRobotPreTask, SeerMessageType.DO_ROBOT_PRE_TASK);
    }

    private void routeComm(String des, SeerMessageType type) throws IOException {
        BaseResponseComm(des, type, routeComm);
    }

    private void routeComm(String des, Object obj, SeerMessageType type) throws IOException {
        BaseResponseComm(des, obj, type, routeComm);
    }

    private void BaseResponseComm(String des, Object obj, SeerMessageType type, Rgv60Comm rgv60Comm) throws IOException {
        BaseResponseComm(des, obj, 1, type, rgv60Comm);
    }

    private void BaseResponseComm(String des, Object obj, int no, SeerMessageType type, Rgv60Comm rgv60Comm) throws IOException {
        String message = JSON.toJSONString(obj);
        BaseResponseComm(des, message, no, type, rgv60Comm);
    }

    private void BaseResponseComm(String des, String message, int no, SeerMessageType type, Rgv60Comm rgv60Comm) throws IOException {
        try {
            RgvLogger.debug(LoggerType.COMM_DATA, null, des, "发送数据:" + message, SeerCommManage.class);
            String result = comm(des, new SeerSendMessage(type.getSendValue(), WcsFuncUtil.changeData(message), no),
                    rgv60Comm, 5);
            SeerNormalResponse response = JSON.parseObject(result, SeerNormalResponse.class);
            if (response.getRet_code() == 0) {
                RgvLogger.debug(LoggerType.COMM_DATA, null, des, "成功", this.getClass());
            } else {
                throw new Rgv60CommFailException(response.getRet_code(), response.getErr_msg());
            }
        } catch (IOException e) {
            RgvLogger.info(LoggerType.ERROR, null, des, "通讯失败", SeerCommManage.class);
            throw e;
        }
    }

    private void BaseResponseComm(String des, SeerMessageType type, Rgv60Comm rgv60Comm) throws IOException {
        try {
            String result = comm(des, new SeerSendMessage(type.getSendValue()), rgv60Comm, 5);
            SeerNormalResponse response = JSON.parseObject(result, SeerNormalResponse.class);
            if (response.getRet_code() == 0) {
                RgvLogger.debug(LoggerType.COMM_DATA, null, des, "成功", this.getClass());
            } else {
                throw new Rgv60CommFailException(response.getRet_code(), response.getErr_msg());
            }
        } catch (IOException e) {
            RgvLogger.info(LoggerType.ERROR, null, des, "通讯失败", SeerCommManage.class);
            throw e;
        }
    }

    private void BaseResponseComm(String des, int no, SeerMessageType type, Rgv60Comm rgv60Comm) throws IOException {
        try {
            String result = comm(des, new SeerSendMessage(type.getSendValue(), no), rgv60Comm, 5);
            SeerNormalResponse response = JSON.parseObject(result, SeerNormalResponse.class);
            if (response.getRet_code() == 0) {
                RgvLogger.debug(LoggerType.COMM_DATA, null, des, "成功", this.getClass());
            } else {
                throw new Rgv60CommFailException(response.getRet_code(), response.getErr_msg());
            }
        } catch (IOException e) {
            RgvLogger.info(LoggerType.ERROR, null, des, "通讯失败", SeerCommManage.class);
            throw e;
        }
    }

    private <T> T comm(String des, Object obj, SeerMessageType type, Rgv60Comm rgv60Comm, Class<T> clazz)
            throws IOException {
        String result = null;
        try {
            String message = JSON.toJSONString(obj);
            RgvLogger.debug(LoggerType.COMM_DATA, null, des + " 发送数据", message, SeerCommManage.class);
            result = comm(des, new SeerSendMessage(type.getSendValue(), WcsFuncUtil.changeData(message)), rgv60Comm,
                    5);
            RgvLogger.debug(LoggerType.COMM_DATA, null, des + " 接收数据", result, this.getClass());
            return JSONObject.parseObject(result, clazz);
        } catch (IOException e) {
            RgvLogger.info(LoggerType.ERROR, null, des, "通讯失败", SeerCommManage.class);
            throw e;
        } catch (Throwable e) {
            RgvLogger.error(LoggerType.ERROR, null, des, result, SeerCommManage.class);
            throw e;
        }
    }

    private <T> T comm(String des, SeerMessageType type, Rgv60Comm rgv60Comm, Class<T> clazz) throws IOException {
        String result = null;
        try {
            result = comm(des, new SeerSendMessage(type.getSendValue()), rgv60Comm, 5);
            RgvLogger.debug(LoggerType.COMM_DATA, null, des + " 接收数据", result, this.getClass());
            return JSONObject.parseObject(result, clazz);
        } catch (IOException e) {
            RgvLogger.info(LoggerType.ERROR, null, des, "通讯失败", SeerCommManage.class);
            throw e;
        } catch (Throwable e) {
            RgvLogger.error(LoggerType.ERROR, null, des, result, SeerCommManage.class);
            throw e;
        }
    }

    private JSONObject comm(String des, SeerMessageType type, Rgv60Comm rgv60Comm) throws IOException {
        String result = null;
        try {
            result = comm(des, new SeerSendMessage(type.getSendValue()), rgv60Comm, 5);
            RgvLogger.debug(LoggerType.COMM_DATA, null, des + " 接收数据", result, this.getClass());
            return JSONObject.parseObject(result);
        } catch (IOException e) {
            RgvLogger.info(LoggerType.ERROR, null, des, "通讯失败", SeerCommManage.class);
            throw e;
        } catch (Throwable e) {
            RgvLogger.error(LoggerType.ERROR, null, des, result, SeerCommManage.class);
            throw e;
        }
    }

    private JSONObject comm(String des, Object obj, SeerMessageType type, Rgv60Comm rgv60Comm) throws IOException {
        String result = null;
        try {
            String message = JSON.toJSONString(obj);
            RgvLogger.debug(LoggerType.COMM_DATA, null, des + " 发送数据", message, SeerCommManage.class);
            result = comm(des, new SeerSendMessage(type.getSendValue(), WcsFuncUtil.changeData(message)), rgv60Comm,
                    5);
            RgvLogger.debug(LoggerType.COMM_DATA, null, des + " 接收数据", result, this.getClass());
            return JSONObject.parseObject(result);
        } catch (IOException e) {
            RgvLogger.info(LoggerType.ERROR, null, des, "通讯失败", SeerCommManage.class);
            throw e;
        } catch (Throwable e) {
            RgvLogger.error(LoggerType.ERROR, null, des, result, SeerCommManage.class);
            throw e;
        }
    }

    /**
     * modbus tcp one send and one receive
     * @param des
     * @param message
     * @return
     * @throws IOException
     */
    private String comm(String des, SeerSendMessage message, Rgv60Comm rgv60Comm, int resendCount) throws IOException {
        return rgv60Comm.comm(des, message, resendCount);
    }

    /**
     * 抢占控制权
     * @return
     */
    public void lockRobot(String nickName, int reSendNum) throws IOException {
        try {
            LockRobot lockRobot = new LockRobot();
            lockRobot.setNick_name(nickName);
            RgvLogger.info(LoggerType.INFO, series, "抢占控制权", lockRobot.toString(), this.getClass());
            BaseResponseComm("抢占控制权", lockRobot, SeerMessageType.LOCK_ROBOT, configComm);
        } catch (IOException e) {
            WcsFuncUtil.delay(100);

            if (reSendNum == 0) {
                throw e;
            } else {
                lockRobot(nickName, --reSendNum);
            }
        }
    }

    /**
     * 查询当前控制权所有者
     * @return
     */
    public RobotControl checkRobotAutor() throws IOException {
        return comm("查询当前控制权所有者", SeerMessageType.CHECK_ROBOT_AUTHOR,
                robotStatComm, RobotControl.class);
    }

    /**
     * 释放控制权
     * @return
     */
    public void releaseRobot() throws IOException {
        RgvLogger.info(LoggerType.INFO, series, "释放控制权", null, this.getClass());
        BaseResponseComm("释放控制权", SeerMessageType.RELEASE_ROBOT, configComm);
    }

    /**
     * 获取小车定位状态
     */
    public RobotPositioningStatus getRobotPositionStatus() throws IOException {
        return comm("获取小车定位状态", SeerMessageType.CHECK_ROBOT_POSITION_STATE,
                robotStatComm, RobotPositioningStatus.class);
    }

    /**
     * 获取小车位置信息
     * @return
     */
    public RobotPositionInfo getRobotPositionInfo() throws IOException {
        return comm("获取小车位置信息", SeerMessageType.CHECK_ROBOT_POSITION_INFO,
                robotStatComm, RobotPositionInfo.class);
    }

    /**
     * 查询机器人任务状态
     * @return
     */
    public RobotTaskStatus getRobotTaskStatus(RobotTaskStatusRtn robotTask) throws IOException {
        return comm("查询机器人任务状态", robotTask, SeerMessageType.CHECK_ROBOT_TASK,
                robotStatComm, RobotTaskStatus.class);
    }

    /**
     * 查询批量数据1
     * @return
     */
    public JSONObject getRobotInfo1() throws IOException {
        return comm("查询批量数据1", SeerMessageType.CHECK_ROBOT_INFO1,
                robotStatComm);
    }

    /**
     * 重定位
     * @return
     * @throws IOException
     */
    public JSONObject calibrationLocation(CalibrationLocation calibrationLocation) throws IOException {
        RgvLogger.info(LoggerType.INFO, series, "重定位", calibrationLocation.toString(), this.getClass());
        return comm("重定位", calibrationLocation, SeerMessageType.CALIBRATION_ROBOT,
                controlComm);
    }

    /**
     * 确认定位正确
     * @return
     * @throws IOException
     */
    public void confirmRobotPostion(int reSendNum) throws IOException {
        try {
            RgvLogger.info(LoggerType.INFO, series, "确认定位正确", null, this.getClass());
            BaseResponseComm("确认定位正确", SeerMessageType.CONFIRM_ROBOT_POSITION,
                    controlComm);
        } catch (IOException e) {
            WcsFuncUtil.delay(100);

            if (reSendNum == 0) {
                throw e;
            } else {
                confirmRobotPostion(--reSendNum);
            }
        }
    }

    /**
     * 设置DO状态
     * @return
     * @throws IOException
     */
    public JSONObject setDigitalOutputStatsForRobot(DigitalOutput digitalOutput) throws IOException {
        RgvLogger.info(LoggerType.INFO, series, "设置DO状态", digitalOutput.toString(), this.getClass());
        return comm("设置DO状态", digitalOutput, SeerMessageType.SET_DIGITAL_OUTPUT_TO_ROBOT,
                otherComm);
    }

    /**
     * 查询机器人载入的地图以及储存的地图
     * @return
     * @throws IOException
     */
    public JSONObject checkRobotMaps() throws IOException {
        return comm("查询机器人载入的地图以及储存的地图", SeerMessageType.CHECK_LOAD_MAPS,
                robotStatComm);
    }

    /**
     * 查询机器人IO状态
     * @return
     * @throws IOException
     */
    public RobotIO checkRobotIO() throws IOException {
        return comm("查询机器人IO状态", SeerMessageType.CHECK_ROBOT_IO,
                robotStatComm, RobotIO.class);
    }

    /**
     * 切换载入的地图
     * @param mapName
     */
    public void changeRobotMap(String mapName) throws IOException {
        ChangeMap changeMap = new ChangeMap();
        changeMap.setMap_name(mapName);
        RgvLogger.info(LoggerType.INFO, series, "切换载入的地图", changeMap.toString(), this.getClass());
        BaseResponseComm("切换载入的地图", changeMap, 1, SeerMessageType.CHANGE_MAP,
                controlComm);
    }

    /**
     * 清除机器人当前所有报错
     */
    public void clearAllFault() throws IOException {
        RgvLogger.info(LoggerType.INFO, series, "清除机器人当前所有报错", null, this.getClass());
        BaseResponseComm("清除机器人当前所有报错", 1, SeerMessageType.CLEAR_ALL_ERROR,
                configComm);
    }

    /**
     * 设置第三方 Error
     */
    public void setRobotError(String msg) throws IOException {
        SetErrorToRobot setErrorToRobot = new SetErrorToRobot();
        setErrorToRobot.setMsg(msg);
        RgvLogger.info(LoggerType.INFO, series, "设置第三方 Error", setErrorToRobot.toString(), this.getClass());
        BaseResponseComm("设置第三方 Error", setErrorToRobot, 1, SeerMessageType.SET_ERROR_TO_ROBOT,
                configComm);
    }

    /**
     * 上传地图
     * @param mapData
     * @throws IOException
     */
    public void sendMap(String mapData) throws IOException {
        RgvLogger.info(LoggerType.INFO, series, "上传地图", mapData, this.getClass());
        BaseResponseComm("上传地图", mapData, 1, SeerMessageType.UP_LOAD_MAP, configComm);
    }

    /**
     * 下载地图
     * @param mapName
     * @return
     * @throws IOException
     */
    public JSONObject downloadMap(String mapName) throws IOException {
        String jsonData = "{"+  "\"map_name\"" + ":" + "\"" + mapName + "\"" + "}";
        Object parse = JSONObject.parse(jsonData);
        RgvLogger.info(LoggerType.INFO, series, "下载地图", jsonData, this.getClass());
        return comm("下载地图", parse, SeerMessageType.DOWNLOAD_MAP, configComm);
    }


    /**
     * 获取当前导航任务状态
     * @return
     */
    public JSONObject checkRobotCurrentTask() throws IOException {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("simple", false);
        JSONObject resultObj = comm("获取当前导航任务状态", jsonObject, SeerMessageType.CHECK_ROBOT_CURRENT_TASK_STATUS, robotStatComm);
        if (resultObj.getInteger("ret_code") == 0) {
            return resultObj;
        }

        throw new Rgv60CommFailException(resultObj.getInteger("ret_code"),
                "获取当前导航任务状态 失败:" + resultObj.getString("err_msg"));
    }

    /**
     * 删除地图
     * @param mapName
     * @throws IOException
     */
    public void deleteMap(String mapName) throws IOException {
        String jsonData = "{"+  "\"map_name\"" + ":" + "\"" + mapName + "\"" + "}";
        Object parse = JSONObject.parse(jsonData);
        RgvLogger.info(LoggerType.INFO, series, "下载地图", jsonData, this.getClass());
        BaseResponseComm("删除地图", parse, 1, SeerMessageType.DELETE_MAP, configComm);
    }

    /**
     * 开环运动
     * @return
     */
    public void motionReq(OpenCircularMove openCircularMove) throws IOException {
        BaseResponseComm("开环运动", openCircularMove, SeerMessageType.OPEN_CIRCLE_MOVE, controlComm);
    }

    /**
     * 销毁socket
     */
    public void destroy() {
        routeComm.close();
        configComm.close();
        controlComm.close();
        robotStatComm.close();
        otherComm.close();
    }
}
